﻿using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;

namespace GameDesigner
{
    public class AnimatorStateMachine : IAnimationHandler
    {
        private Animator animator;
        private float elapsedTime;
        private float normalizedTime;
        private int stateHash;
        private readonly StateLayer layer;
        private readonly AnimationLayerMixerPlayable animationLayerMixer; //状态机层
        private float layerBlendWeight;
        private LayerBlendState layerBlendState;

        public AnimatorStateMachine(Animator animator, StateLayer layer)
        {
            this.animator = animator;
            this.layer = layer;
            var graph = animator.playableGraph;
            var output = (AnimationPlayableOutput)graph.GetOutput(0);
            var animatorController = (AnimatorControllerPlayable)output.GetSourcePlayable();
            animationLayerMixer = (AnimationLayerMixerPlayable)animatorController.GetInput(0);
        }

        public void SetParams(params object[] args) //中途修改动画对象用
        {
            animator = args[0] as Animator;
        }

        public void OnInitializeBegin(IStateMachine stateMachine)
        {
            ApplyLayerMask();
            ApplyLayerWeight(false);
        }

        public void OnInitializeEnd(IStateMachine stateMachine)
        {
        }

        public void OnPlayAnimation(State state, StateAction stateAction)
        {
            var clipName = stateAction.clipName;
            stateAction.animTime = 0f;
            stateHash = Animator.StringToHash(clipName);
            animator.speed = state.animSpeed;
            elapsedTime = 0f;
            normalizedTime = 0f;
            StateAction.SetBlendTreeParameter(stateAction, animator);
            if (state.isCrossFade)
                animator.CrossFade(clipName, state.duration, layer.LayerIndex);
            else
                animator.Play(clipName, layer.LayerIndex, 0f);
        }

        public bool OnAnimationUpdate(State state, StateAction stateAction, StateMachineUpdateMode currMode)
        {
            var isPlaying = true;
            if (currMode == StateMachineUpdateMode.Update)
            {
                elapsedTime += Time.deltaTime * state.animSpeed;
                if (stateAction.length <= 0f)
                {
                    var stateInfo = animator.GetCurrentAnimatorStateInfo(layer.LayerIndex);
                    if (stateHash == stateInfo.shortNameHash) //当在切换自身状态时, stateInfo获取到的还是上一个动画的状态信息, 所以会导致当前进度错乱, 导致鬼畜问题
                        stateAction.length = stateInfo.length;
                    else
                        return isPlaying; //往下执行就会变成 n/0问题
                }
                normalizedTime = elapsedTime / stateAction.length;
                stateAction.animTime = normalizedTime / 1f * 100f;
            }
            return isPlaying;
        }

        public void OnAnimationEnd(State state, StateAction stateAction) { }

        public void ApplyLayerMask()
        {
            if (layer.AvatarMask != null)
                animationLayerMixer.SetLayerMaskFromAvatarMask((uint)layer.LayerIndex, layer.AvatarMask);
            else
                animationLayerMixer.SetLayerMaskFromAvatarMask((uint)layer.LayerIndex, layer.DefaultMask);
        }

        public void ApplyLayerWeight(bool isCrossFade = true)
        {
            if (isCrossFade)
                layerBlendState = layer.Weight > 0f ? LayerBlendState.EnterLayerWeight : LayerBlendState.ExitLayerWeight;
            else
                animator.SetLayerWeight(layer.LayerIndex, layerBlendWeight = layer.Weight);
        }

        public void OnStateEnter(State state) { }

        public void OnStateUpdate(State state, StateMachineUpdateMode currMode)
        {
            if (currMode == StateMachineUpdateMode.Update)
            {
                if (layerBlendState == LayerBlendState.EnterLayerWeight)
                    layerBlendState = LayerBlendState.UpdateEnterLayerWeight;
                else if (layerBlendState == LayerBlendState.ExitLayerWeight)
                    layerBlendState = LayerBlendState.UpdateExitLayerWeight;
                if (layerBlendState == LayerBlendState.UpdateEnterLayerWeight)
                {
                    if (layerBlendWeight < 1f)
                    {
                        layerBlendWeight += state.duration * 60f * Time.deltaTime;
                        layerBlendWeight = layerBlendWeight > 1f ? 1f : layerBlendWeight;
                    }
                    else layerBlendState = LayerBlendState.None;
                    animator.SetLayerWeight(layer.LayerIndex, layerBlendWeight);
                }
                else if (layerBlendState == LayerBlendState.UpdateExitLayerWeight)
                {
                    if (layerBlendWeight > 0f)
                    {
                        layerBlendWeight -= state.duration * 60f * Time.deltaTime;
                        layerBlendWeight = layerBlendWeight < 0f ? 0f : layerBlendWeight;
                    }
                    else layerBlendState = LayerBlendState.None;
                    animator.SetLayerWeight(layer.LayerIndex, layerBlendWeight);
                }
            }
        }

        public void OnStateExit(State state) { }
    }
}